<?php

namespace DreamCat\FrameCore\Factory\Impl\Dispatchers;

use DreamCat\Container\EntryFactory;
use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use function FastRoute\simpleDispatcher;

/**
 * 默认的路由派发器工厂
 * @author vijay
 */
class DefaultDispatcherFactory implements EntryFactory
{
    /**
     * @Config router
     * @var array 配置信息
     */
    private $config;

    /**
     * 设置配置，考虑使用不了注入时的工厂复用
     * @param array $config 要设定的配置
     * @return static 对象本身
     */
    public function setConfig(array $config): DefaultDispatcherFactory
    {
        $this->config = $config;
        return $this;
    }

    /**
     * 创建实体
     * @param string $id 实体标识
     * @param string $arg 额外参数
     * @return mixed 实体
     */
    public function create(string $id, string $arg): Dispatcher
    {
        if (!$this->config) {
            $this->config = ["uris" => []];
        }
        return simpleDispatcher(
            [
                $this,
                "routeDefinitionCallback",
            ]
        );
    }

    /**
     * 路由定义回调函数
     * @param RouteCollector $routeCollector 路由收集器
     * @return void
     */
    public function routeDefinitionCallback(RouteCollector $routeCollector): void
    {
        if (isset($this->config["prefix"])) {
            $prefix = $this->fixUriPrefix($this->config["prefix"]);
        } else {
            $prefix = "/";
        }
        if ($prefix != "/") {
            $routeCollector->addGroup($prefix, $this->parseUrisCallback($this->config["uris"]));
        } else {
            $this->parseUrisCallback($this->config["uris"])($routeCollector);
        }
    }

    /**
     * 修正uri，确保前面有/而后面没有
     * @param string $uriPrefix uri地址
     * @return string
     */
    public function fixUriPrefix(string $uriPrefix): string
    {
        if (!strlen($uriPrefix)) {
            return "/";
        }
        if ($uriPrefix[0] != "/") {
            $uriPrefix = "/{$uriPrefix}";
        }
        $len = strlen($uriPrefix);
        if ($len > 1 && $uriPrefix[$len - 1] == "/") {
            $uriPrefix = substr($uriPrefix, 0, -1);
        }
        return $uriPrefix;
    }

    /**
     * 生成解析uri配置的回调函数
     * @param array $uris uri配置
     * @return callable 回调函数
     */
    public function parseUrisCallback(array $uris): callable
    {
        return function (RouteCollector $routeCollector) use ($uris) {
            foreach ($uris as $uri => $cfg) {
                if (is_string($cfg)) {
                    $routeCollector->get($this->fixUriPrefix($uri), $cfg);
                } elseif (isset($cfg["method"]) && isset($cfg["handle"])) {
                    $routeCollector->addRoute($cfg["method"] ?? "GET", $this->fixUriPrefix($uri), $cfg["handle"]);
                } else {
                    $routeCollector->addGroup($this->fixUriPrefix($uri), $this->parseUrisCallback($cfg));
                }
            }
        };
    }
}

# end of file
